{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "c62d87ba-4f4e-4f38-ace1-d7a52a38013d",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    },
    "tags": []
   },
   "source": [
    "# Quantum Kernels and Support Vector Machines"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "038449da-e37e-4764-b527-169962901836",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    },
    "tags": []
   },
   "source": [
    "## Credit card fraud detection - simple use case"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7d36d7c2-53ea-41a5-a636-3fe65a3a3026",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    },
    "tags": []
   },
   "source": [
    "#### QSVM on Kaggle labeled data as means to classify and detect credit card fraudulant transactions\n",
    "\n",
    "\n",
    "- Quantum Machine Learning (QML) is the aspect of research that explores the consequences of implementing machine learning on a quantum computer\n",
    "- SVM is a supervised machine learning method widely used for multiple labeled data classification.\n",
    "- SVM algorithm can be enhanced even on a noisy intermediate scale quantum computer (NISQ) by introducing kernel method.\n",
    "\n",
    "  It can be restructured so that it can exploit the properties of large dimensionality of quantum Hilbert space\n",
    "- In this demo we will present a simple use case where Quantum SVM (QSVM) algorithm will be implemented on credit card labeled data to detect fraudulent transactions\n",
    "- We will leverage Classiqs proprietary QSVM library and core capabilities to explore the rising potential in enhancing security applications\n",
    "\n",
    "\n",
    "\n",
    "#### Demostration is based on recent work published on August 2022 [[1](#HBC)]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b2543d1e",
   "metadata": {},
   "source": [
    "*In this demo, besides the `classiq` package, we will use the `sklearn` package*"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "b920a4cb",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T15:06:24.407392Z",
     "iopub.status.busy": "2024-05-07T15:06:24.406919Z",
     "iopub.status.idle": "2024-05-07T15:06:30.685580Z",
     "shell.execute_reply": "2024-05-07T15:06:30.684757Z"
    }
   },
   "outputs": [],
   "source": [
    "%%capture\n",
    "! pip install scikit-learn\n",
    "! pip install seaborn"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "30533f38-de79-4541-a91c-5bd1b2d40256",
   "metadata": {
    "tags": []
   },
   "source": [
    "*Importing the required resources:*"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "89e069db-667d-4a8c-ac7d-93603edd969c",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T15:06:30.688887Z",
     "iopub.status.busy": "2024-05-07T15:06:30.688413Z",
     "iopub.status.idle": "2024-05-07T15:06:32.631670Z",
     "shell.execute_reply": "2024-05-07T15:06:32.630191Z"
    },
    "slideshow": {
     "slide_type": "skip"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "# General Imports\n",
    "# Visualisation Imports\n",
    "import matplotlib.pyplot as plt\n",
    "import numpy as np\n",
    "import pandas as pd\n",
    "\n",
    "# Scikit Imports\n",
    "import sklearn\n",
    "from sklearn import datasets\n",
    "from sklearn.decomposition import PCA\n",
    "from sklearn.model_selection import train_test_split\n",
    "from sklearn.preprocessing import MinMaxScaler, StandardScaler\n",
    "from sklearn.svm import SVC\n",
    "from sklearn.utils import shuffle"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "976eee82-dab2-48a5-8715-090556f2f5b2",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T15:06:32.647033Z",
     "iopub.status.busy": "2024-05-07T15:06:32.646596Z",
     "iopub.status.idle": "2024-05-07T15:06:33.013277Z",
     "shell.execute_reply": "2024-05-07T15:06:33.012517Z"
    },
    "slideshow": {
     "slide_type": "skip"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "## For TSNE visualisation\n",
    "from numpy import linalg\n",
    "from numpy.linalg import norm\n",
    "from scipy.spatial.distance import pdist, squareform\n",
    "from sklearn.manifold import TSNE\n",
    "\n",
    "# We'll hack a bit with the t-SNE code in sklearn 0.15.2.\n",
    "from sklearn.metrics.pairwise import pairwise_distances\n",
    "from sklearn.preprocessing import scale\n",
    "\n",
    "# Random state.\n",
    "RS = 20150101\n",
    "\n",
    "import matplotlib\n",
    "import matplotlib.colors as colors\n",
    "\n",
    "# We'll use matplotlib for graphics.\n",
    "import matplotlib.patheffects as PathEffects\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "# We import seaborn to make nice plots.\n",
    "try:\n",
    "    import seaborn as sns\n",
    "except ModuleNotFoundError:\n",
    "    palette = np.array(\n",
    "        [\n",
    "            (0.4, 0.7607843137254902, 0.6470588235294118),\n",
    "            (0.9882352941176471, 0.5529411764705883, 0.3843137254901961),\n",
    "            (0.5529411764705883, 0.6274509803921569, 0.796078431372549),\n",
    "            (0.9058823529411765, 0.5411764705882353, 0.7647058823529411),\n",
    "            (0.6509803921568628, 0.8470588235294118, 0.32941176470588235),\n",
    "            (1.0, 0.8509803921568627, 0.1843137254901961),\n",
    "            (0.8980392156862745, 0.7686274509803922, 0.5803921568627451),\n",
    "            (0.7019607843137254, 0.7019607843137254, 0.7019607843137254),\n",
    "        ]\n",
    "    )\n",
    "else:\n",
    "    sns.set_style(\"darkgrid\")\n",
    "    sns.set_palette(\"muted\")\n",
    "    sns.set_context(\"notebook\", font_scale=1.5, rc={\"lines.linewidth\": 2.5})\n",
    "    palette = np.array(sns.color_palette(\"Set2\"))\n",
    "\n",
    "import warnings\n",
    "\n",
    "warnings.filterwarnings(\"ignore\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "cf293355-283b-4d95-8d60-4a1e82d3093d",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T15:06:33.018687Z",
     "iopub.status.busy": "2024-05-07T15:06:33.018222Z",
     "iopub.status.idle": "2024-05-07T15:06:34.622153Z",
     "shell.execute_reply": "2024-05-07T15:06:34.621448Z"
    },
    "slideshow": {
     "slide_type": "skip"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "## Classiq imports\n",
    "\n",
    "from classiq import Pauli, construct_qsvm_model, execute, show, synthesize\n",
    "from classiq.applications.qsvm import QSVMFeatureMapEntanglement"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c1bce27f-bdd5-4265-b6b2-6a7e2e771bb2",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    },
    "tags": []
   },
   "source": [
    "## The Data"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f8423973-8440-4c68-939f-2bc4edce97f3",
   "metadata": {
    "tags": []
   },
   "source": [
    "The dataset contains **transactions made by credit cards in September 2013 by European cardholders**.\n",
    "\n",
    "This dataset presents transactions that occurred in **two days**, where there were **492 frauds** out of **284,807 transactions**.\n",
    "The dataset is highly unbalanced, the positive class (frauds) account for 0.172% of all transactions.\n",
    "\n",
    "Data properties:\n",
    "- Database contains only numeric input variables which are the result of a PCA transformation.\n",
    "- Due to confidentiality issues, original features are not provided.\n",
    "- Features V1, V2, \u2026 V28 are the principal components obtained with PCA.\n",
    "- The only features which have not been transformed with PCA are 'Time' and 'Amount'.\n",
    "- Feature 'Time' contains the seconds elapsed between each transaction and the first transaction in the dataset.\n",
    "- The feature 'Amount' is the transaction Amount.\n",
    "- Feature 'Class' is the response variable and it takes value 1 in case of fraud and 0 otherwise.\n",
    "\n",
    "\n",
    "##### Data is freely available through Kaggle [[2](#Kaggle)]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c573138e-8b2d-4395-8474-4262711bf192",
   "metadata": {
    "tags": []
   },
   "source": [
    "### Loading the Kaggle \"Credit Card Fraud Detection\" data set"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "84239db8-5506-4d74-83ca-68807d63da57",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T15:06:34.625733Z",
     "iopub.status.busy": "2024-05-07T15:06:34.624772Z",
     "iopub.status.idle": "2024-05-07T15:06:34.659941Z",
     "shell.execute_reply": "2024-05-07T15:06:34.659052Z"
    },
    "slideshow": {
     "slide_type": "fragment"
    },
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>Time</th>\n",
       "      <th>V1</th>\n",
       "      <th>V2</th>\n",
       "      <th>V3</th>\n",
       "      <th>V4</th>\n",
       "      <th>V5</th>\n",
       "      <th>V6</th>\n",
       "      <th>V7</th>\n",
       "      <th>V8</th>\n",
       "      <th>V9</th>\n",
       "      <th>...</th>\n",
       "      <th>V21</th>\n",
       "      <th>V22</th>\n",
       "      <th>V23</th>\n",
       "      <th>V24</th>\n",
       "      <th>V25</th>\n",
       "      <th>V26</th>\n",
       "      <th>V27</th>\n",
       "      <th>V28</th>\n",
       "      <th>Amount</th>\n",
       "      <th>Class</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>0</td>\n",
       "      <td>-1.359807</td>\n",
       "      <td>-0.072781</td>\n",
       "      <td>2.536347</td>\n",
       "      <td>1.378155</td>\n",
       "      <td>-0.338321</td>\n",
       "      <td>0.462388</td>\n",
       "      <td>0.239599</td>\n",
       "      <td>0.098698</td>\n",
       "      <td>0.363787</td>\n",
       "      <td>...</td>\n",
       "      <td>-0.018307</td>\n",
       "      <td>0.277838</td>\n",
       "      <td>-0.110474</td>\n",
       "      <td>0.066928</td>\n",
       "      <td>0.128539</td>\n",
       "      <td>-0.189115</td>\n",
       "      <td>0.133558</td>\n",
       "      <td>-0.021053</td>\n",
       "      <td>149.62</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>0</td>\n",
       "      <td>1.191857</td>\n",
       "      <td>0.266151</td>\n",
       "      <td>0.166480</td>\n",
       "      <td>0.448154</td>\n",
       "      <td>0.060018</td>\n",
       "      <td>-0.082361</td>\n",
       "      <td>-0.078803</td>\n",
       "      <td>0.085102</td>\n",
       "      <td>-0.255425</td>\n",
       "      <td>...</td>\n",
       "      <td>-0.225775</td>\n",
       "      <td>-0.638672</td>\n",
       "      <td>0.101288</td>\n",
       "      <td>-0.339846</td>\n",
       "      <td>0.167170</td>\n",
       "      <td>0.125895</td>\n",
       "      <td>-0.008983</td>\n",
       "      <td>0.014724</td>\n",
       "      <td>2.69</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>1</td>\n",
       "      <td>-1.358354</td>\n",
       "      <td>-1.340163</td>\n",
       "      <td>1.773209</td>\n",
       "      <td>0.379780</td>\n",
       "      <td>-0.503198</td>\n",
       "      <td>1.800499</td>\n",
       "      <td>0.791461</td>\n",
       "      <td>0.247676</td>\n",
       "      <td>-1.514654</td>\n",
       "      <td>...</td>\n",
       "      <td>0.247998</td>\n",
       "      <td>0.771679</td>\n",
       "      <td>0.909412</td>\n",
       "      <td>-0.689281</td>\n",
       "      <td>-0.327642</td>\n",
       "      <td>-0.139097</td>\n",
       "      <td>-0.055353</td>\n",
       "      <td>-0.059752</td>\n",
       "      <td>378.66</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>1</td>\n",
       "      <td>-0.966272</td>\n",
       "      <td>-0.185226</td>\n",
       "      <td>1.792993</td>\n",
       "      <td>-0.863291</td>\n",
       "      <td>-0.010309</td>\n",
       "      <td>1.247203</td>\n",
       "      <td>0.237609</td>\n",
       "      <td>0.377436</td>\n",
       "      <td>-1.387024</td>\n",
       "      <td>...</td>\n",
       "      <td>-0.108300</td>\n",
       "      <td>0.005274</td>\n",
       "      <td>-0.190321</td>\n",
       "      <td>-1.175575</td>\n",
       "      <td>0.647376</td>\n",
       "      <td>-0.221929</td>\n",
       "      <td>0.062723</td>\n",
       "      <td>0.061458</td>\n",
       "      <td>123.50</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>2</td>\n",
       "      <td>-1.158233</td>\n",
       "      <td>0.877737</td>\n",
       "      <td>1.548718</td>\n",
       "      <td>0.403034</td>\n",
       "      <td>-0.407193</td>\n",
       "      <td>0.095921</td>\n",
       "      <td>0.592941</td>\n",
       "      <td>-0.270533</td>\n",
       "      <td>0.817739</td>\n",
       "      <td>...</td>\n",
       "      <td>-0.009431</td>\n",
       "      <td>0.798278</td>\n",
       "      <td>-0.137458</td>\n",
       "      <td>0.141267</td>\n",
       "      <td>-0.206010</td>\n",
       "      <td>0.502292</td>\n",
       "      <td>0.219422</td>\n",
       "      <td>0.215153</td>\n",
       "      <td>69.99</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "<p>5 rows \u00d7 31 columns</p>\n",
       "</div>"
      ],
      "text/plain": [
       "   Time        V1        V2        V3        V4        V5        V6        V7  \\\n",
       "0     0 -1.359807 -0.072781  2.536347  1.378155 -0.338321  0.462388  0.239599   \n",
       "1     0  1.191857  0.266151  0.166480  0.448154  0.060018 -0.082361 -0.078803   \n",
       "2     1 -1.358354 -1.340163  1.773209  0.379780 -0.503198  1.800499  0.791461   \n",
       "3     1 -0.966272 -0.185226  1.792993 -0.863291 -0.010309  1.247203  0.237609   \n",
       "4     2 -1.158233  0.877737  1.548718  0.403034 -0.407193  0.095921  0.592941   \n",
       "\n",
       "         V8        V9  ...       V21       V22       V23       V24       V25  \\\n",
       "0  0.098698  0.363787  ... -0.018307  0.277838 -0.110474  0.066928  0.128539   \n",
       "1  0.085102 -0.255425  ... -0.225775 -0.638672  0.101288 -0.339846  0.167170   \n",
       "2  0.247676 -1.514654  ...  0.247998  0.771679  0.909412 -0.689281 -0.327642   \n",
       "3  0.377436 -1.387024  ... -0.108300  0.005274 -0.190321 -1.175575  0.647376   \n",
       "4 -0.270533  0.817739  ... -0.009431  0.798278 -0.137458  0.141267 -0.206010   \n",
       "\n",
       "        V26       V27       V28  Amount  Class  \n",
       "0 -0.189115  0.133558 -0.021053  149.62      0  \n",
       "1  0.125895 -0.008983  0.014724    2.69      0  \n",
       "2 -0.139097 -0.055353 -0.059752  378.66      0  \n",
       "3 -0.221929  0.062723  0.061458  123.50      0  \n",
       "4  0.502292  0.219422  0.215153   69.99      0  \n",
       "\n",
       "[5 rows x 31 columns]"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import pathlib\n",
    "\n",
    "path = (\n",
    "    pathlib.Path(__file__).parent.resolve()\n",
    "    if \"__file__\" in locals()\n",
    "    else pathlib.Path(\".\")\n",
    ")\n",
    "input_file = path / \"../resources/creditcard.csv\"\n",
    "# comma delimited file as input\n",
    "kaggle_full_set = pd.read_csv(input_file, header=0)\n",
    "# presnting first 5 lines:\n",
    "kaggle_full_set.head()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ffb1bcee-4ba8-4410-8017-e671411d7601",
   "metadata": {
    "tags": []
   },
   "source": [
    "<div class=\"alert alert-block alert-success\">\n",
    "\n",
    "## 1. Data Preprocessing\n",
    "\n",
    "</div>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "366dab5d-7042-4ed8-9213-97e8b23df4fe",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "source": [
    "### Selecting training and testing data sets\n",
    "\n",
    "To proceed with the analysis, we sub-sample the dataset to make it manageable for near-term quantum simulations"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "2f699508-1313-4ed1-a62e-2977c2f495fa",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T15:06:34.664603Z",
     "iopub.status.busy": "2024-05-07T15:06:34.664238Z",
     "iopub.status.idle": "2024-05-07T15:06:34.672031Z",
     "shell.execute_reply": "2024-05-07T15:06:34.669832Z"
    },
    "slideshow": {
     "slide_type": "fragment"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "TRAIN_NOMINAL_SIZE = 100\n",
    "TRAIN_FRAUD_SIZE = 25\n",
    "\n",
    "TEST_NOMINAL_SIZE = 50\n",
    "TEST_FRAUD_SIZE = 10\n",
    "\n",
    "PREDICTION_NOMINAL_SIZE = 50\n",
    "PREDICTION_FRAUD_SIZE = 10\n",
    "\n",
    "SHUFFLE_DATA = False"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "74ba1d64-1dd3-4195-89d4-4b2cf7ac259f",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T15:06:34.675769Z",
     "iopub.status.busy": "2024-05-07T15:06:34.675364Z",
     "iopub.status.idle": "2024-05-07T15:06:34.679986Z",
     "shell.execute_reply": "2024-05-07T15:06:34.679529Z"
    },
    "slideshow": {
     "slide_type": "skip"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "## Seperating nominal (\"legit\") from fraud:\n",
    "all_fraud_set = kaggle_full_set.loc[kaggle_full_set[\"Class\"] == 1]\n",
    "all_nominal_set = kaggle_full_set.loc[kaggle_full_set[\"Class\"] == 0]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "84701072-4956-49cc-8340-dd9eaecac36f",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T15:06:34.682463Z",
     "iopub.status.busy": "2024-05-07T15:06:34.681944Z",
     "iopub.status.idle": "2024-05-07T15:06:34.685285Z",
     "shell.execute_reply": "2024-05-07T15:06:34.684754Z"
    },
    "slideshow": {
     "slide_type": "skip"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "## Optionally shuffle data before selctive sets\n",
    "if SHUFFLE_DATA:\n",
    "    all_fraud_set = shuffle(all_fraud_set, random_state=1234)\n",
    "    all_nominal_set = shuffle(all_nominal_set, random_state=1234)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "d4acdef7-bdcc-4bf7-8da0-013bac83a4e9",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T15:06:34.689377Z",
     "iopub.status.busy": "2024-05-07T15:06:34.688716Z",
     "iopub.status.idle": "2024-05-07T15:06:34.697962Z",
     "shell.execute_reply": "2024-05-07T15:06:34.697364Z"
    },
    "slideshow": {
     "slide_type": "skip"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "## Selecting data subsets\n",
    "selected_training_set = pd.concat(\n",
    "    [all_nominal_set[:TRAIN_NOMINAL_SIZE], all_fraud_set[:TRAIN_FRAUD_SIZE]]\n",
    ")\n",
    "selected_testing_set = pd.concat(\n",
    "    [\n",
    "        all_nominal_set[TRAIN_NOMINAL_SIZE : TRAIN_NOMINAL_SIZE + TEST_NOMINAL_SIZE],\n",
    "        all_fraud_set[TRAIN_FRAUD_SIZE : TRAIN_FRAUD_SIZE + TEST_FRAUD_SIZE],\n",
    "    ]\n",
    ")\n",
    "selected_prediction_set = pd.concat(\n",
    "    [\n",
    "        all_nominal_set[\n",
    "            TRAIN_NOMINAL_SIZE\n",
    "            + TEST_NOMINAL_SIZE : TRAIN_NOMINAL_SIZE\n",
    "            + TEST_NOMINAL_SIZE\n",
    "            + PREDICTION_NOMINAL_SIZE\n",
    "        ],\n",
    "        all_fraud_set[\n",
    "            TRAIN_FRAUD_SIZE\n",
    "            + TEST_FRAUD_SIZE : TRAIN_FRAUD_SIZE\n",
    "            + TEST_FRAUD_SIZE\n",
    "            + PREDICTION_FRAUD_SIZE\n",
    "        ],\n",
    "    ]\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "b97f4b59-d39f-43bd-a250-e0cfd89cc6ce",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T15:06:34.702729Z",
     "iopub.status.busy": "2024-05-07T15:06:34.701218Z",
     "iopub.status.idle": "2024-05-07T15:06:34.711283Z",
     "shell.execute_reply": "2024-05-07T15:06:34.710520Z"
    },
    "slideshow": {
     "slide_type": "skip"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "## Seperating relevant features data (excluding the \"Time\" colom) from label data\n",
    "\n",
    "kaggle_headers = list(kaggle_full_set.columns.values)  # all headers\n",
    "feature_cols = kaggle_headers[1:-1]  # excluding Time and Class headers\n",
    "label_col = kaggle_headers[-1]  # marking Class header as label\n",
    "\n",
    "selected_training_data = selected_training_set.loc[:, feature_cols]\n",
    "selected_training_labels = selected_training_set.loc[:, label_col]\n",
    "\n",
    "selected_testing_data = selected_testing_set.loc[:, feature_cols]\n",
    "selected_testing_labels = selected_testing_set.loc[:, label_col]\n",
    "\n",
    "selected_prediction_data = selected_prediction_set.loc[:, feature_cols]\n",
    "selected_prediction_true_labels = selected_prediction_set.loc[:, label_col]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "484f0025-c9a7-47ad-89d0-5f5e5a83fa5e",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "source": [
    "### Visualization of the selected data sets with t-SNE\n",
    "\n",
    "t-SNE is a technique for dimensionality reduction that is particularly suited for the visualization of high-dimensional datasets"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "902d33f0-33d4-42ef-8e5f-2d990962b597",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T15:06:34.716871Z",
     "iopub.status.busy": "2024-05-07T15:06:34.715687Z",
     "iopub.status.idle": "2024-05-07T15:06:34.724604Z",
     "shell.execute_reply": "2024-05-07T15:06:34.723862Z"
    },
    "slideshow": {
     "slide_type": "skip"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "def scatter(x, colors):\n",
    "    # We create a scatter plot.\n",
    "    f = plt.figure(figsize=(8, 8))\n",
    "    ax = plt.subplot(aspect=\"equal\")\n",
    "    sc = ax.scatter(x[:, 0], x[:, 1], lw=0, s=40, c=palette[colors.astype(np.int32)])\n",
    "    plt.xlim(-25, 25)\n",
    "    plt.ylim(-25, 25)\n",
    "    ax.axis(\"off\")\n",
    "    ax.axis(\"tight\")\n",
    "\n",
    "    # We add the labels for each digit.\n",
    "    txts = []\n",
    "    labels = [\"Nominal\", \"Fraud\"]\n",
    "    for i in range(2):\n",
    "        # Position of each label.\n",
    "        xtext, ytext = np.median(x[colors == i, :], axis=0)\n",
    "        txt = ax.text(xtext, ytext, labels[i], fontsize=24)\n",
    "        txt.set_path_effects(\n",
    "            [PathEffects.Stroke(linewidth=5, foreground=\"w\"), PathEffects.Normal()]\n",
    "        )\n",
    "        txts.append(txt)\n",
    "\n",
    "    return"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "02df1717-74b3-4072-a720-4e32b07d6004",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "source": [
    "#### TSNE Visualization of train data\n",
    "\n",
    "We observe that visually t-SNE shows a separation between nominal and anomalous samples.\n",
    "\n",
    "However, **sole visualization map does not allow to track all fraudulent transactions.**\n",
    "\n",
    "**This demonstrates the challenge of high-quality fraud detection.**\n",
    "\n",
    "For the sake of quick demonstration we subselcetd a very small percentage of the data. Better logic for sub selecting the training and testing datasets will effect quality of results."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "bd2c3494-cd33-4f51-85f6-8877d8af63f6",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T15:06:34.730027Z",
     "iopub.status.busy": "2024-05-07T15:06:34.728797Z",
     "iopub.status.idle": "2024-05-07T15:06:35.775054Z",
     "shell.execute_reply": "2024-05-07T15:06:35.774309Z"
    },
    "slideshow": {
     "slide_type": "fragment"
    },
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "image/png": "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tum5557TrbfeOthv165devLJJ/Wd73xHGzduVH5+vr761a/qtdde07Zt2/Tkk0/q0ksvHez/l7/8Rbt27RrTz322n/70p3rrrbeUl5enL33pS3r55Ze1fft2Pffcc/rYxz42OIVcX1+vBx980OO9bDabLr/8cn33u9/V888/r927d6usrEx79uzR448/rttuu23wflu2bNFDDz00bj8H/I8ACABhoqa7Xb86tEl9zuEjTmfYXU6trTzg91oKElK99rHZbPrSl740+P1jjz3mcVTKm29961uD78vNmTNHL774ou644w4lJyfLarVq6tSp+t73vqfHHntscLrywIEDeuqpp7ze+/Dhw8rOztbf/vY3/eu//qumTZumlJQUzZw5U9/+9rd19913D/b9+c9/rqefflqLFi3SP/7xD33qU59SUVGRUlJStHTpUj366KNDQuCrr7563j/zuQ4cOKAlS5bo+eef1+c+9zkVFxcrOTlZM2fO1De/+c0hI46vvfba4Grsc02fPl2vvPKKHnnkEX3oQx/StGnTFBcXJ8MwFBsbq5UrV+o//uM/9Jvf/GYwUP/lL38ZcbQToYkACABh4vWaw3KYI/+FfrY9zVUKlVNAr776as2ZM0eS1N3drV/96lfndZ9jx47p8OHDkk4Hyx/96Edu3+9buXKl/vmf/3nw+xdeeMGnZ3zrW99y+97cbbfdNvh1R0eHDMPQ/fffP2INFotFd9xxx+D3mzZt8un5voiKitKDDz7o9me/+eablZGRIen0qOuBAyP/Y6CgoEAFBQVen7d8+fLBfRzb2tq0c+fO86wcgUYABIAwcayjyad+TtOl0Ih/kmEY+upXvzr4/ZNPPqna2tpR3+fsUbTly5erpKTEY/8Pf/jDg1/v27fP6zMTExOHjNqdKz09XVOnTh38fvHixR4XWSxatGjwfbpDhw6Ny/uPknThhRcqJyfHbbthGFq5cuXg9+MR2G644YbBr9etWzfm+yEwWAUMAGEiyoetTSSpNDlblhDauuPCCy/UihUrtGXLFg0MDOhnP/uZ/ud//mdU93jttdcGv77ooou89i8oKNCUKVNUUVEx+PmPfexjbvtfdNFFXrepmTRpko4dOyZJHsOidDpQZmRkqLGxUdLpLWc8rUj21WWXXea1z5n3Ls881xe9vb3avXu3mpqa1NPTM2QE+eTJk4Nf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",
      "text/plain": [
       "<Figure size 800x800 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "proj = TSNE(random_state=RS).fit_transform(selected_training_data)\n",
    "scatter(proj, selected_training_labels)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6fdc171a-f27d-447a-abd7-a81f0022a711",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "source": [
    "#### TSNE Visualization of test data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "08035ccb-e703-4728-92c2-a7c63f2d81b2",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T15:06:35.780083Z",
     "iopub.status.busy": "2024-05-07T15:06:35.778862Z",
     "iopub.status.idle": "2024-05-07T15:06:36.421666Z",
     "shell.execute_reply": "2024-05-07T15:06:36.420882Z"
    },
    "slideshow": {
     "slide_type": "fragment"
    },
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "image/png": "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qirdc889euONNwbMLyws1He/+10tW7YsdGzjxo269957dejQoX5z77vvPq1cuVJf//rXVVZWFjbDr371qyG3gXn55Zd11113SZIuvvhi/fjHP9a2bdv07//+79qzZ8+A+bNnz9Ztt92miy66SJJkuD1yXfxZBTf+WYb7g792nnnmGT377LPavHmz/H7/oPlycnJ0ySWX6Oabb9bEiRPD/joAAMPHEjDi2uc+97nQ2aqdO3eG9og7FU1NTfr0pz+tb3zjG4OWP+l4sfvNb36jj3zkI1qzZs2wP/vAgQO65pprBi1/klRTU6NbbrlFu3fvliStXbtWN99884Dyd8L69et1ww03hDZ6Hitr167V9ddfP2j5k6T3339ft99+u/7617+Gjhm5xXLNWdZv3gMPPKA333wzbPmTjv9+P/nkk1q9erW2b98+Nr8AAHA4CiDiWlZWlm6++ebQzz/84Q8jlo2hBAIB3X777aEiYhiGLrvsMj399NPaunWrXnvtNd19992hpdze3l7deeed2rZt25Cf3dfXpzvuuEMtLS0688wz9aMf/Ujr16/X2rVr9fWvf115eXmSJL/fr7vvvlvbt2/XXXfdJcMwdMkll+iXv/ylXnnlFf31r3/VP//zPysxMVGSVFdXFzrDNxYOHTqkr371qwoEArrooov06KOPasOGDXrppZf0b//2byoqKgrNvffee0NLx5JklC8a8HmlpaW65ZZb9LOf/Uzr16/X1q1btXXrVj377LO68847VVhYKElqaWnRl770pdBejQCA0aMAIu595jOf0YQJEyQdLy/PPPPMqD/rqaee0ubNmyVJbrdbP/3pT/XDH/5Q8+fPV0pKivLz83XzzTfrxRdf1KxZsyQdL4333HPPkDeh+Hw+7d27V//6r/+qxx9/XJdeeqmKi4s1ceJEffazn9Wvf/1reb1eSVJFRYU+97nPKRgM6pFHHtGDDz6o5cuXq7CwUNOnT9dXv/pVffe73w199po1axQMBkf96z7Znj175Pf79bOf/UwPPfSQzjvvPBUVFWnSpEm66aab9Pvf/15ZWVmSpLa2Nr311lthP+v+++/XmjVr9JWvfEXnnnuuiouLlZKSopSUFM2ePVu33nqrnnvuOc2ePVuSdOzYMf39738fk18HADgZBRBxLzU1VV/84hdDPz/00EPy+Xyj+qy//e1vodc3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",
      "text/plain": [
       "<Figure size 800x800 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "proj = TSNE(random_state=RS).fit_transform(selected_testing_data)\n",
    "scatter(proj, selected_testing_labels)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fde31b9c-bca0-4777-af53-283718ba78f6",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    },
    "tags": []
   },
   "source": [
    "### Reduce dimensions\n",
    "\n",
    "**Convert original features into fewer features to match the number of Qubits**\n",
    "\n",
    "We perform dimensionality reduction to match\n",
    "the number of features with the number of qubits used in\n",
    "simulation.  For this, we use principal component analysis\n",
    "and keep only the first *N_DIM* principal components.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "0df5673a-bdc1-4286-9d0d-246b356ee17d",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T15:06:36.426752Z",
     "iopub.status.busy": "2024-05-07T15:06:36.425451Z",
     "iopub.status.idle": "2024-05-07T15:06:36.430684Z",
     "shell.execute_reply": "2024-05-07T15:06:36.429982Z"
    },
    "slideshow": {
     "slide_type": "fragment"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "## choosing data dimension to encode\n",
    "N_DIM = 3"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "88123513-9449-4bd0-90b0-4ab0fc85a622",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T15:06:36.435478Z",
     "iopub.status.busy": "2024-05-07T15:06:36.434262Z",
     "iopub.status.idle": "2024-05-07T15:06:36.444808Z",
     "shell.execute_reply": "2024-05-07T15:06:36.443575Z"
    },
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "sample_train = selected_training_data.values.tolist()\n",
    "sample_test = selected_testing_data.values.tolist()\n",
    "sample_predict = selected_prediction_data.values.tolist()\n",
    "\n",
    "# Reduce dimensions\n",
    "\n",
    "pca = PCA(n_components=N_DIM).fit(sample_train)\n",
    "sample_train = pca.transform(sample_train)\n",
    "sample_test = pca.transform(sample_test)\n",
    "sample_predict = pca.transform(sample_predict)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "eb9bf97d-4c77-4bca-ba7b-11d2ff32ab41",
   "metadata": {
    "tags": []
   },
   "source": [
    "### Normalize\n",
    "\n",
    "We use feature-wise standard scaling, i.e.\n",
    "we subtract the mean and scale by the standard deviation for each feature"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "d1d11dfe-80c7-4dba-aa08-25e7f6abcd34",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T15:06:36.449652Z",
     "iopub.status.busy": "2024-05-07T15:06:36.448397Z",
     "iopub.status.idle": "2024-05-07T15:06:36.455009Z",
     "shell.execute_reply": "2024-05-07T15:06:36.454341Z"
    },
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "# Normalize\n",
    "std_scale = StandardScaler().fit(sample_train)\n",
    "sample_train = std_scale.transform(sample_train)\n",
    "sample_test = std_scale.transform(sample_test)\n",
    "sample_predict = std_scale.transform(sample_predict)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "42dd19da-a8c9-42c7-91f9-6eae92ed9cdf",
   "metadata": {
    "tags": []
   },
   "source": [
    "### Scale\n",
    "\n",
    "Scaling each feature to a range between -$\\pi$ and $\\pi$."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "898bd956-fc38-4618-b705-f61abd056718",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T15:06:36.459894Z",
     "iopub.status.busy": "2024-05-07T15:06:36.458663Z",
     "iopub.status.idle": "2024-05-07T15:06:36.465991Z",
     "shell.execute_reply": "2024-05-07T15:06:36.465297Z"
    },
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "# Scale\n",
    "samples = np.append(sample_train, sample_test, axis=0)\n",
    "samples = np.append(samples, sample_predict, axis=0)\n",
    "minmax_scale = MinMaxScaler((-np.pi, np.pi)).fit(samples)\n",
    "FRAUD_TRAIN_DATA = minmax_scale.transform(sample_train)\n",
    "FRAUD_TEST_DATA = minmax_scale.transform(sample_test)\n",
    "FRAUD_PREDICT_DATA = minmax_scale.transform(sample_predict)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "098ab007-187d-44ce-a959-70de500a65f3",
   "metadata": {
    "tags": []
   },
   "source": [
    "### Final preprocessed data set"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "6f497a48-9588-424a-bd39-7c1a436cdaa9",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T15:06:36.470923Z",
     "iopub.status.busy": "2024-05-07T15:06:36.469716Z",
     "iopub.status.idle": "2024-05-07T15:06:36.475068Z",
     "shell.execute_reply": "2024-05-07T15:06:36.474403Z"
    }
   },
   "outputs": [],
   "source": [
    "FRAUD_TRAIN_LABELS = np.array(selected_training_labels.values.tolist())\n",
    "FRAUD_TEST_LABELS = np.array(selected_testing_labels.values.tolist())"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d16034bb-8790-4115-ae75-04842af78300",
   "metadata": {
    "tags": []
   },
   "source": [
    "<div class=\"alert alert-block alert-success\">\n",
    "\n",
    "## 2. Map data to Hilbert Space\n",
    "\n",
    "</div>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "aebed4dd-f8a0-4708-9c14-1347d12e2f91",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    },
    "tags": []
   },
   "source": [
    "### FeatureMap design with Classiq\n",
    "\n",
    "The feature map is a parameterized quantum circuit, which can be described as a unitary transformation $\\mathbf{U_\\phi}(\\mathbf{x})$ on n qubits.\n",
    "\n",
    "Since the data may be non-linearly-separable in the original space, **the feature map circuit will map the classical data into Hilbert space**.\n",
    "\n",
    "The choice of which feature map circuit to use is key and may depend on the given dataset we want to classify.\n",
    "We will leverage Classiq feature map design capabilities"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b90b3bb2-ae0b-4b08-86fa-62f7a3fae0ae",
   "metadata": {
    "tags": []
   },
   "source": [
    "#### Choose existing feature map type (or build our own)\n",
    "As an example we can choose from the well known the Second-order Pauli-Z evolution encoding circuit with 2 repetitions OR the bloch sphere circuit encoding"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "7bfd814e-0c8e-4c4f-b020-3402212c0003",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T15:06:36.480090Z",
     "iopub.status.busy": "2024-05-07T15:06:36.478728Z",
     "iopub.status.idle": "2024-05-07T15:06:36.484967Z",
     "shell.execute_reply": "2024-05-07T15:06:36.484287Z"
    },
    "slideshow": {
     "slide_type": "fragment"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "## Choosing \"Pauli ZZ\" feature map encodign with 2 repetitions:\n",
    "pauli_zz_feature_map_function_name = \"pauli_feature_map\"\n",
    "pauli_zz_kwargs = {\n",
    "    \"paulis\": [[Pauli.Z], [Pauli.Z, Pauli.Z]],\n",
    "    \"entanglement\": QSVMFeatureMapEntanglement.FULL,\n",
    "    \"alpha\": 2,\n",
    "    \"reps\": 2,\n",
    "    \"feature_dimension\": N_DIM,\n",
    "}\n",
    "\n",
    "## Choosing \"bloch sphere\" feature map encoding:\n",
    "bloch_sphere_feature_map_function_name = \"bloch_sphere_feature_map\"\n",
    "bloch_sphere_kwargs = {\"bloch_feature_dimension\": N_DIM}"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0464ebb0-d0b7-4d29-bba7-0717133ea9d6",
   "metadata": {
    "tags": []
   },
   "source": [
    "<div class=\"alert alert-block alert-success\">\n",
    "\n",
    "## 3. Execute QSVM\n",
    "\n",
    "</div>\n",
    "\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b97d3434-7b1b-46c3-bd7d-42194f402d21",
   "metadata": {},
   "source": [
    "Quantum Support Vector Machines is the **Quantum version of SVM - a data classification method which separates the data using a hyperplane**.\n",
    "\n",
    "The algorithm will perform the following steps [[3](#3)]:\n",
    "\n",
    "1. **Estimating the kernel matrix**\n",
    "\n",
    "    - A quantum feature map, $\\phi(\\mathbf{x})$, naturally gives rise to a quantum kernel, $k(\\mathbf{x}_i,\\mathbf{x}_j)= \\phi(\\mathbf{x}_j)^\\dagger\\phi(\\mathbf{x}_i)$, which can be seen as a measure of similarity: $k(\\mathbf{x}_i,\\mathbf{x}_j)$ is large when $\\mathbf{x}_i$ and $\\mathbf{x}_j$ are close.\n",
    "\n",
    "    - When considering finite data, we can represent the quantum kernel as a matrix:\n",
    "    $K_{ij} = \\left| \\langle \\phi^\\dagger(\\mathbf{x}_j)| \\phi(\\mathbf{x}_i) \\rangle \\right|^{2}$.\n",
    "\n",
    "        **We can calculate each element of this kernel matrix on a quantum computer by calculating the transition amplitude**:\n",
    "        $$\n",
    "        \\left| \\langle \\phi^\\dagger(\\mathbf{x}_j)| \\phi(\\mathbf{x}_i) \\rangle \\right|^{2} =\n",
    "        \\left| \\langle 0^{\\otimes n} | \\mathbf{U_\\phi^\\dagger}(\\mathbf{x}_j) \\mathbf{U_\\phi}(\\mathbf{x_i}) | 0^{\\otimes n} \\rangle \\right|^{2}\n",
    "        $$\n",
    "\n",
    "        This provides us with an estimate of the quantum kernel matrix, which will be used in the support vector classification.\n",
    "\n",
    "\n",
    "2. **Optimizing** the dual problem by **using the classical SVM algorithm to generate a separating Hyperplane and classify the data**\n",
    "$$ L_D(\\alpha) = \\sum_{i=1}^t \\alpha_i - \\frac{1}{2} \\sum_{i,j=1}^t y_i y_j \\alpha_i \\alpha_j K(\\vec{x}_i \\vec{x}_j) $$\n",
    "    - Where $t$ is the amount of data points\n",
    "    - the $\\vec{x}_i$s are the data points\n",
    "    - $y_i$ is the label $\\in \\{-1,1\\}$ of each data point\n",
    "    - $K(\\vec{x}_i \\vec{x}_j)$ is the kernel matrix element between the $i$ and $j$ datapoints\n",
    "    - We optimize over the $\\alpha$s\n",
    "    - We expect most of the $\\alpha$s to be $0$. The $\\vec{x}_i$s that correspond to non-zero $\\alpha_i$ are called the Support Vectors."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c78dc4c2-6010-4c70-a4d8-896c59e9aeb1",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    },
    "tags": []
   },
   "source": [
    "### Setup Classiq QSVM model:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "dae3fe08-b1a4-4f8a-a62c-7b4e18e9edab",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T15:06:36.490182Z",
     "iopub.status.busy": "2024-05-07T15:06:36.488880Z",
     "iopub.status.idle": "2024-05-07T15:06:36.503146Z",
     "shell.execute_reply": "2024-05-07T15:06:36.502374Z"
    },
    "slideshow": {
     "slide_type": "fragment"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "## Setting QSVM model with feature map and data\n",
    "\n",
    "QSVM_FRAUD_PAULI_ZZ = construct_qsvm_model(\n",
    "    train_data=FRAUD_TRAIN_DATA.tolist(),\n",
    "    train_labels=FRAUD_TRAIN_LABELS.tolist(),\n",
    "    test_data=FRAUD_TEST_DATA.tolist(),\n",
    "    test_labels=FRAUD_TEST_LABELS.tolist(),\n",
    "    predict_data=FRAUD_PREDICT_DATA.tolist(),\n",
    "    feature_map_function_name=pauli_zz_feature_map_function_name,\n",
    "    **pauli_zz_kwargs\n",
    ")\n",
    "\n",
    "QSVM_FRAUD_BLOCH_SHPERE = construct_qsvm_model(\n",
    "    train_data=FRAUD_TRAIN_DATA.tolist(),\n",
    "    train_labels=FRAUD_TRAIN_LABELS.tolist(),\n",
    "    test_data=FRAUD_TEST_DATA.tolist(),\n",
    "    test_labels=FRAUD_TEST_LABELS.tolist(),\n",
    "    predict_data=FRAUD_PREDICT_DATA.tolist(),\n",
    "    feature_map_function_name=bloch_sphere_feature_map_function_name,\n",
    "    **bloch_sphere_kwargs\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f74ba26f",
   "metadata": {},
   "source": [
    "### Synthesize our model and explore the generated quantum circuit\n",
    "Once we constructed our qsvm model - we synthesize and view the quantum circuit that encodes our data.\n",
    "For this we will use `classiq` built-in `synthesize` and `show` functions:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "id": "fccf5210-84ee-4a9f-bc66-20ac655b6f93",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T15:06:36.508303Z",
     "iopub.status.busy": "2024-05-07T15:06:36.507102Z",
     "iopub.status.idle": "2024-05-07T15:06:36.516205Z",
     "shell.execute_reply": "2024-05-07T15:06:36.515481Z"
    }
   },
   "outputs": [],
   "source": [
    "from classiq import write_qmod\n",
    "\n",
    "write_qmod(QSVM_FRAUD_PAULI_ZZ, \"credit_card_fraud\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "id": "e8d304b8-e19b-4478-b69c-0510c5950dfd",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T15:06:36.521183Z",
     "iopub.status.busy": "2024-05-07T15:06:36.519916Z",
     "iopub.status.idle": "2024-05-07T15:06:37.996902Z",
     "shell.execute_reply": "2024-05-07T15:06:37.996214Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Opening: https://platform.classiq.io/circuit/cf7cf831-a096-4fbe-aa88-7cc888a02adb?version=0.41.0.dev39%2B79c8fd0855\n"
     ]
    }
   ],
   "source": [
    "qprog = synthesize(QSVM_FRAUD_PAULI_ZZ)\n",
    "show(qprog)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c1bcb48d-84c6-4c81-b475-cba637e71c86",
   "metadata": {
    "jp-MarkdownHeadingCollapsed": true,
    "slideshow": {
     "slide_type": "slide"
    },
    "tags": []
   },
   "source": [
    "### Run QSVM and analyze results\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5573de8c-6c44-4843-96fe-3fa72292742c",
   "metadata": {
    "tags": []
   },
   "source": [
    "### Train and test the data\n",
    "\n",
    "1. Build the train and test quantum kernel matrices.\n",
    "    1. **For each pair of datapoints in the training dataset $\\mathbf{x}_{i},\\mathbf{x}_j$, apply the feature map and measure the transition probability**}: $ K_{ij} = \\left| \\langle 0 | \\mathbf{U}^\\dagger_{\\Phi(\\mathbf{x_j})} \\mathbf{U}_{\\Phi(\\mathbf{x_i})} | 0 \\rangle \\right|^2 $.\n",
    "    2. **For each training datapoint $\\mathbf{x_i}$ and testing point $\\mathbf{y_i}$, apply the feature map and measure the transition probability**: $ K_{ij} = \\left| \\langle 0 | \\mathbf{U}^\\dagger_{\\Phi(\\mathbf{y_i})} \\mathbf{U}_{\\Phi(\\mathbf{x_i})} | 0 \\rangle \\right|^2 $.\n",
    "2. Use the train and **test quantum kernel matrices in a classical support vector machine classification algorithm**.\n",
    "\n",
    "\n",
    "Executing QSVM is done though the `execute` function.\n",
    "The execution results will include the accuracy of the classification and the predicted labels for the predict data.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "id": "02bb556d-f986-48e7-8802-22d660a89571",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T15:06:37.999624Z",
     "iopub.status.busy": "2024-05-07T15:06:37.999116Z",
     "iopub.status.idle": "2024-05-07T15:12:08.260765Z",
     "shell.execute_reply": "2024-05-07T15:12:08.260123Z"
    },
    "slideshow": {
     "slide_type": "fragment"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "results = execute(qprog).result()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "id": "195c71a9",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T15:12:08.263763Z",
     "iopub.status.busy": "2024-05-07T15:12:08.263301Z",
     "iopub.status.idle": "2024-05-07T15:12:08.267041Z",
     "shell.execute_reply": "2024-05-07T15:12:08.266379Z"
    },
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "quantum kernel classification test score:  0.87\n"
     ]
    }
   ],
   "source": [
    "print(\n",
    "    \"quantum kernel classification test score:  %0.2f\"\n",
    "    % (results[0].value[\"test_score\"])\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8bbe012d",
   "metadata": {},
   "source": [
    "**Result seems quite good. It's a good start! Let's analyze it further:**"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c9108115-05ba-4c35-b21e-167a61e745ca",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "source": [
    "#### Compare testing accuracy results to classical kernels:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "id": "db1d3128",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T15:12:08.269574Z",
     "iopub.status.busy": "2024-05-07T15:12:08.269096Z",
     "iopub.status.idle": "2024-05-07T15:12:08.288000Z",
     "shell.execute_reply": "2024-05-07T15:12:08.286788Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "linear kernel classification test score:  1.00\n",
      "poly kernel classification test score:  1.00\n",
      "rbf kernel classification test score:  1.00\n",
      "sigmoid kernel classification test score:  0.95\n"
     ]
    }
   ],
   "source": [
    "classical_kernels = [\"linear\", \"poly\", \"rbf\", \"sigmoid\"]\n",
    "\n",
    "for ckernel in classical_kernels:\n",
    "    classical_svc = SVC(kernel=ckernel)\n",
    "    classical_svc.fit(FRAUD_TRAIN_DATA, FRAUD_TRAIN_LABELS)\n",
    "    classical_score = classical_svc.score(\n",
    "        FRAUD_TEST_DATA, np.array(FRAUD_TEST_LABELS.tolist())\n",
    "    )\n",
    "\n",
    "    print(\"%s kernel classification test score:  %0.2f\" % (ckernel, classical_score))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d09158ab",
   "metadata": {},
   "source": [
    "Given our simple naive training and testing set example, the **Quantum kernel gets results \"at least as good as\" the classical kernels** -  can be interpetted as **prominsing sign** and encourage deeper research. Baring in mind the following:\n",
    "- Quantum kernel machine algorithms only have the potential of quantum advantage over classical approaches if the corresponding quantum kernel is hard to estimate classically (a necessary and not always sufficient condition to obtain a quantum advantage)\n",
    "\n",
    "- However, it was proven recently [[4](#4)] that learning problems exist for which learners with access to quantum kernel methods have a quantum advantage over all classical learners."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3e95e852",
   "metadata": {},
   "source": [
    "### Predict data\n",
    "\n",
    "\n",
    "  Finally, we may predict unlabeled data by calculating the kernel matrix of the new datum with respect to the support vectors\n",
    "    $$ \\text{Predicted Label}(\\vec{s}) = \\text{sign} \\left( \\sum_{i=1}^t y_i \\alpha_i^* K(\\vec{x}_i , \\vec{s}) + b \\right) $$\n",
    "        - Where $\\vec{s}$ is the datapoint to be classified\n",
    "        - $\\alpha_i^*$ are the optimized $\\alpha$s\n",
    "        - And $b$ is the bias."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "id": "f07fe84d-30fe-4041-a427-9fb3f639559c",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T15:12:08.290968Z",
     "iopub.status.busy": "2024-05-07T15:12:08.290453Z",
     "iopub.status.idle": "2024-05-07T15:12:08.296059Z",
     "shell.execute_reply": "2024-05-07T15:12:08.295463Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.9333333333333333"
      ]
     },
     "execution_count": 26,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "true_labels = np.array(selected_prediction_true_labels.values.tolist())\n",
    "sklearn.metrics.accuracy_score(results[0].value[\"predicted_labels\"], true_labels)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "94caf317-f2e9-4a0f-87ff-858552025aaa",
   "metadata": {
    "slideshow": {
     "slide_type": "skip"
    },
    "tags": []
   },
   "source": [
    "<div class=\"alert alert-block alert-success\">\n",
    "\n",
    "\n",
    "## Quantum advantage is possible:\n",
    "\n",
    "**QSVM has the *potential* for enhancing performance, accuracy and even efficiency** in resources:\n",
    "\n",
    "- **There are limitations to the successful classical solutions when the feature space becomes large**, and the kernel functions become computationally expensive to estimate\n",
    "\n",
    "- For **certain types of data** a classifier that **exploits the quantum feature space shows better results**.\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "**A necessary condition to obtain a quantum advantage is that the kernel cannot be estimated classically.** Since quantum computers are not expected to be classically simulable there is a very large design space to explore.\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "In the future it becomes intriguing to find suitable feature maps for this technique with provable quantum advantages while providing significant improvement on real world data sets. With the ubiquity of kernel methods in machine learning, we are optimistic that our techniques will produce applications even beyond binary classification.\n",
    "\n",
    "\n",
    "</div>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d2c67384-d50d-4fd7-859f-30ba916fdb96",
   "metadata": {
    "slideshow": {
     "slide_type": "skip"
    },
    "tags": []
   },
   "source": [
    "\n",
    "\n",
    "## References\n",
    "\n",
    "\n",
    "<a id='1'>[1]</a>: [Oleksandr Kyriienko, Einar B. Magnusson \"Unsupervised quantum machine learning for fraud detection\"](https://arxiv.org/abs/2208.01203)\n",
    "\n",
    "<a id='Kaggle'>[2]</a>: [Kaggle dataset - Credit Card Fraud Detection\n",
    "Anonymized credit card transactions labeled as fraudulent or genuine](https://www.kaggle.com/datasets/mlg-ulb/creditcardfraud)\n",
    "\n",
    "<a id='3'>[3]</a>: [Havl&#237;&#269;ek, V., C&#243;rcoles, A.D., Temme, K. et al. Supervised learning with quantum-enhanced feature spaces. Nature 567, 209-212 (2019)](https://doi.org/10.1038/s41586-019-0980-2)\n",
    "\n",
    "<a id='4'>[4]</a>: [Liu et al. A rigorous and robust quantum speed-up in supervised machine learning (2020)](https://arxiv.org/pdf/2010.02174.pdf)\n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.9"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
